﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using IntoNails.DataAccess;
using IntoNails.Infrastructure;
using IntoNails.Infrastructure.DataAccess;
using IntoNails.Infrastructure.ViewModels;
using IntoNails.Models;

namespace IntoNails.Modules.CategoryManagement.ViewModels
{
    /// <summary>
    /// Object represents the left category-tree view-model.
    /// </summary>
    public partial class CategoryWorkspaceViewModel : ICategoryTreeViewModel
    {
        #region Fields

        private CategoryRepository _categoryRepository;

        private ObservableCollection<ITreeNodeViewModel> _categoryTreeNodeCollection;

        #endregion

        #region Properties

        public ObservableCollection<ITreeNodeViewModel> CategoryTreeNodeCollection
        {
            get { return this._categoryTreeNodeCollection; }
            set
            {
                this._categoryTreeNodeCollection = value;
                base.RaisePropertyChanged(() => CategoryTreeNodeCollection);
            }
        }

        public TreeNodeViewModel SelectedCategoryNode { get; private set; }

        public TreeNodeViewModel RootCategoryNode { get; private set; }

        #endregion

        #region Commands

        public ICommand CategoryTreeNodeSelectedCommand { get; private set; }

        #endregion

        #region Command Action Handlers

        private void OnCategoryTreeNodeSelected(TreeNodeViewModel selectedNode)
        {
            this.SelectedCategoryNode = selectedNode;
        }

        #endregion

        #region Private Helpers

        private void PopulateCategoryTree()
        {
            Category rootCategory = Category.RootCategory;
            this.RootCategoryNode = new TreeNodeViewModel(rootCategory.DisplayName, null, rootCategory, rootCategory);
            this.RootCategoryNode.Selected += this.CategoryNodeOnSelected;

            ////this.SelectedCategoryNode = this.RootCategoryNode;

            this._categoryRepository = new CategoryRepository(GlobalInfoCaches.StartUpPath + @"\Category.dat");
            this._categoryRepository.Added += this.CategoryRepositoryOnAdded;
            this._categoryRepository.Updated += this.CategoryRepositoryOnUpdated;
            this._categoryRepository.Removed += this.CategoryRepositoryOnRemoved;
            this._categoryRepository.Load();

            this._serviceRepository = new ServiceRepository(GlobalInfoCaches.StartUpPath + @"\Service.dat");
            this._serviceRepository.Load();

            // loop the category which has no parent
            foreach (Category category in this._categoryRepository.Get(c => c.Parent.Equals(Category.RootCategory)))
            {
                TreeNodeViewModel categoryNode = new TreeNodeViewModel(category.DisplayName, this.RootCategoryNode,
                                                                       category, category);
                categoryNode.Children.Add(new TreeNodeViewModel("dummy"));
                categoryNode.Selected += this.CategoryNodeOnSelected;
                categoryNode.Expanding += this.CategoryNodeOnExpanding;
                categoryNode.Expanded += this.CategoryNodeOnExpanded;

                this.RootCategoryNode.Children.Add(categoryNode);
            }

            this.RootCategoryNode.IsExpanded = true;

            this.CategoryTreeNodeCollection = new ObservableCollection<ITreeNodeViewModel> { this.RootCategoryNode };
        }

        private void CategoryNodeOnExpanded(object sender, EventArgs eventArgs)
        {
            ////TreeNodeViewModel senderNode = sender as TreeNodeViewModel;
            ////senderNode.is
        }

        private void RefreshCategoryTree()
        {
            this.CategoryTreeNodeCollection.Remove(this.RootCategoryNode);
            this.CategoryTreeNodeCollection.Add(this.RootCategoryNode);
        }

        private void CategoryNodeOnExpanding(TreeNodeViewModel currentNode)
        {
            currentNode.Children.Clear();
            Category associatedCategory = currentNode.AssociatedObject as Category;

            if (associatedCategory == null)
            {
                throw new InvalidCastException("associatedCategory");
            }

            // loop the category which has no parent
            foreach (Category category in this._categoryRepository.Get(c => c.Parent.Equals(associatedCategory)))
            {
                TreeNodeViewModel categoryNode = new TreeNodeViewModel(category.DisplayName, currentNode, category, category);
                categoryNode.Children.Add(new TreeNodeViewModel("dummy"));
                categoryNode.Selected += this.CategoryNodeOnSelected;
                categoryNode.Expanding += this.CategoryNodeOnExpanding;
                categoryNode.Expanded += this.CategoryNodeOnExpanded;

                currentNode.Children.Add(categoryNode);
            }
        }

        private void CategoryNodeOnSelected(TreeNodeViewModel currentNode)
        {
            switch (currentNode.Status)
            {
                case NodeStatus.Selected:
                    this.SelectedCategoryNode = currentNode;

                    IEnumerable<Service> services;

                    if (this.SelectedCategoryNode == this.RootCategoryNode)
                    {
                        services = this._serviceRepository.Get(s => s.IsValid);
                    }
                    else
                    {
                        // loads all service of this category
                        services =
                            this._serviceRepository.Get(
                                s =>
                                s.AssociatedCategory.UniqueCode ==
                                (this.SelectedCategoryNode.AssociatedObject as Category).UniqueCode);
                    }

                    this.ServiceCollection = new ObservableCollection<Service>(services);

                    break;
                case NodeStatus.Added:
                    break;
                case NodeStatus.Edited:
                    break;
                case NodeStatus.Deleted:
                    break;
            }
        }
        
        #endregion

        #region Event Handlers

        private void CategoryRepositoryOnRemoved(object sender, DataEventArgs<Category> e)
        {
            if (e.Result)
            {
                // TODO: write to audit trail...
                this._categoryRepository.Save();
            }
            else
            {
                // TODO: write error log
                throw e.Error;
            }
        }

        private void CategoryRepositoryOnUpdated(object sender, DataUpdatedEventArgs<Category> e)
        {
            if (e.Result)
            {
                // TODO: write to audit trail...

                this._categoryRepository.Save();
            }
            else
            {
                // TODO: write error log
                throw e.Error;
            }
        }

        private void CategoryRepositoryOnAdded(object sender, DataEventArgs<Category> e)
        {
            if (e.Result)
            {
                // TODO: write to audit trail...
                this._categoryRepository.Save();
            }
            else
            {
                // TODO: write error log
                throw e.Error;
            }
        }

        #endregion
    }
}
